﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;

namespace VmcRemote.Common.Power
{
    public class PowerNotificationProvider : IDisposable
    {
        private const int BroadcastLength = 1024;

        private IntPtr hMessageQueue;
        private IntPtr hQuitEvent;
        private Thread listenerThread = null;

        public event EventHandler PowerTransition;

        public PowerNotificationProvider()
        {
            // Create event to stop the thread
            hQuitEvent = CreateEvent(0, true, false, null);
            // Create listener thread
            listenerThread = new Thread(new ThreadStart(WaitThreadProc));

            try
            {
                // Create message queue for power broadcasts
                hMessageQueue = CreateMsgQueue("PowerEventWaitQueue", new MessageQueueOptions());
            }
            catch (MissingMethodException)
            {
                // If we got here, it means the target device does not have CE.NET. This API is not available in Windows CE 3.0
                return;
            }

            listenerThread.Start();

            IntPtr hNotif = RequestPowerNotifications(
                hMessageQueue, PowerEventType.StatusChange | PowerEventType.Resume | PowerEventType.Transition
                );
        }

        void WaitThreadProc()
        {
            // Open our message queue for reading
            MessageQueueOptions opt = new MessageQueueOptions();
            opt.bReadAccess = 1;
            IntPtr hQueue = CreateMsgQueue("PowerEventWaitQueue", opt);

            // While we are not quitting...
            while (WaitForSingleObject(hQuitEvent, 0) != WAIT_OBJECT_0)
            {
                // If there is a message in the queue, read it.
                if (WaitForSingleObject(hMessageQueue, 100) == WAIT_OBJECT_0)
                {
                    // Get the broadcast from the queue.
                    PowerBroadcast broadcast = new PowerBroadcast(BroadcastLength);
                    int numberOfBytes, flags;
                    ReadMsgQueue(hQueue, broadcast.Data, broadcast.Data.Length, out numberOfBytes, 0, out flags);

                    // If this is a transition from the ON state, let everyone know.
                    if ((broadcast.Message & PowerEventType.Transition) == PowerEventType.Transition &&
                        (broadcast.Flags & PowerState.On) == PowerState.On)
                    {
                        // Raise the event to let everyone know about the transition.
                        RaisePowerEvent(broadcast.Message, broadcast.Flags);
                    }
                }
            }
            // Close the handle to the queues.
            CloseHandle(hQueue);
            CloseHandle(hQuitEvent);
        }

        private void RaisePowerEvent(PowerEventType powerEvent, PowerState powerState)
        {
            if (PowerTransition != null)
                PowerTransition(this, EventArgs.Empty);
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose of managed resources.
                if (listenerThread != null)
                {
                    listenerThread.Abort();
                    listenerThread = null;
                }
            }
            // Dispose of unmanaged resources.
            if (hQuitEvent != IntPtr.Zero)
            {
                EventModify(hQuitEvent, EVENT_SET);
                CloseHandle(hQuitEvent);
            }
            if (hMessageQueue != IntPtr.Zero)
            {
                CloseHandle(hMessageQueue);
            }
        }

        ~PowerNotificationProvider()
        {
            Dispose(false);
        }

        #endregion

        #region P/Invoke Stuff

        [DllImport("coredll")]
        static extern IntPtr CreateMsgQueue(string Name, MessageQueueOptions Options);

        [DllImport("coredll")]
        static extern bool ReadMsgQueue(
            IntPtr hMsgQ,
            byte[] lpBuffer,
            int cbBufferSize,
            out int lpNumberOfBytesRead,
            int dwTimeout,
            out int pdwFlags
            );


        [DllImport("coredll")]
        static extern IntPtr CreateEvent(int dwReserved1, bool bManualReset, bool bInitialState, string Name);

        [DllImport("coredll")]
        static extern bool EventModify(IntPtr hEvent, int func);

        [DllImport("coredll")]
        static extern bool CloseHandle(IntPtr h);

        [DllImport("coredll")]
        static extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, PowerEventType Flags);

        [DllImport("coredll")]
        static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

        const int WAIT_OBJECT_0 = 0;
        const int EVENT_SET = 3;
        #endregion
    }
}
